"""
Support for Midea's CCM-18 thermostats.
Author: github.com/ohsc
configuration.yaml
climate:
  - platform: ccm18
    name: ccm18
    host: IP_ADDRESS
    port: 80
    scan_interval: 10
"""
import logging
import math
import time

from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
    HVAC_MODE_COOL,
    HVAC_MODE_DRY,
    HVAC_MODE_FAN_ONLY,
    HVAC_MODE_HEAT,
    HVAC_MODE_AUTO,
    HVAC_MODE_OFF,
    FAN_AUTO,
    FAN_HIGH,
    FAN_MIDDLE,
    FAN_LOW,
    SUPPORT_FAN_MODE,
    SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.const import (
    ATTR_MODE,
    CONF_NAME,
    CONF_HOST,
    CONF_PORT,
    PRECISION_WHOLE,
    TEMP_CELSIUS,
    ATTR_TEMPERATURE,
)

import xmltodict
import requests

_LOGGER = logging.getLogger(__name__)

DEFAULT_TIMEOUT = 5
BASE_URL = "http://{0}:{1}{2}?utsxxx={3}"
CONF_URL_CTRL = "/ctrl.xml"


# ccm18 code
# case 0:	cmode=0x88;break;
# case 1:	cmode=0x84;break;
# case 3:	cmode=0x81;break;
# case 4:	cmode=0x00;break;

# case 10: cfan=0x01;break;
# case 11: cfan=0x02;break;
# case 12: cfan=0x04;break;
# case 13: cfan=0x80;break;
CONST_STATE_CMD_MAP = {
    HVAC_MODE_COOL: 0x88,
    HVAC_MODE_HEAT: 0x84,
    HVAC_MODE_FAN_ONLY: 0x81,
    HVAC_MODE_OFF: 0x00,
    # HVAC_MODE_DRY: 2,
    # HVAC_MODE_AUTO: 5,
}
CONST_FAN_CMD_MAP = {
    FAN_AUTO: 0x80,
    FAN_LOW: 0x04,
    FAN_MIDDLE: 0x02,
    FAN_HIGH: 0x01,
}

SUPPORT_HVAC = list(CONST_STATE_CMD_MAP.keys())
SUPPORT_FAN = list(CONST_FAN_CMD_MAP.keys())


SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE | SUPPORT_FAN_MODE


# pylint: disable=unused-argument
async def async_setup_entry(hass, config_entry, async_add_entities):
    """Setup the Midea thermostats."""
    await hass.async_add_executor_job(pull_add, config_entry, async_add_entities)


def pull_add(config_entry, async_add_entities):
    config = config_entry.data
    """Setup the Midea thermostats."""
    acs = poll_status(config.get(CONF_HOST), config.get(CONF_PORT))

    dev = []
    for ac_name, ac_data in acs.items():
        dev.append(
            Thermostat(
                config.get(CONF_NAME),
                ac_name,
                config.get(CONF_HOST),
                config.get(CONF_PORT),
                ac_data,
            )
        )
    async_add_entities(dev)


def get_base_url(host, port):
    return BASE_URL.format(host, port, CONF_URL_CTRL, math.floor(time.time()))


def poll_one(host, port, acNum):
    resource = get_base_url(host, port) + "&u=" + str(acNum)
    try:
        response = requests.get(resource, timeout=10)
        doc = xmltodict.parse(response.text)
        return doc["response"]["ret"]
    except requests.exceptions.MissingSchema:
        _LOGGER.error(
            "Missing resource or schema in configuration. " "Add http:// to your URL"
        )
        return None
    except requests.exceptions.ConnectionError:
        _LOGGER.error("No route to device at %s", resource)
        return None


def get_online_list(ret):
    if ret is None:
        return None
    cols = ret.strip(",").split(",")
    if len(cols) < 160:
        return None

    inputs = []
    for i in range(0, 32):
        inputs.append(abs(int(cols[128 + i])) & 0xFFFF)

    acs = []
    acs.append(inputs[18])
    acs.append(inputs[19])
    acs.append(inputs[20])
    acs.append(inputs[21])
    acs.append(inputs[17])
    acs.append(inputs[24])
    acs.append(inputs[25])
    acs.append(inputs[26])
    acs.append(inputs[27])
    acs.append(inputs[22])

    online_list = []
    for i in range(0, 64):
        if is_online(i, acs):
            online_list.append(i)
    return online_list


# is ac online
def is_online(acNum, acs):
    status = acs[math.floor(acNum / 16)] & (1 << (acNum % 16))
    return status != 0


#  var micn_en=new Array(
#  0 'Fan-mode',1 'Dry-mode',2 'Heat-mode',3 'Cool-mode', 4 'Auto-mode', 5'Mode-lock',6 '--', 7 'On/Off',
# 8 'High-fan',9 'Medium-fan',10 'Low-fan',11 'Breeze-fan',12 '--',13 '--',14 '--',15 'Auto-fan',
# var miin_en=new Array(
# 0 'System status',1 'UnitStyle-1',2 'UnitStyle-2',3'Set temp.Ts'
# ,4 'Room temp.T1','Evaporator-temp.T2A','Evaporator-temp.T2B'
def get_ac_info(ret):
    cols = ret.strip(",").split(",")
    if len(cols) < 160:
        return None

    mode = HVAC_MODE_OFF
    if int(cols[0]) != 0:
        mode = HVAC_MODE_FAN_ONLY
    if int(cols[1]) != 0:
        mode = HVAC_MODE_DRY
    if int(cols[2]) != 0:
        mode = HVAC_MODE_HEAT
    if int(cols[3]) != 0:
        mode = HVAC_MODE_COOL
    if int(cols[4]) != 0:
        mode = HVAC_MODE_AUTO

    fan = FAN_AUTO
    if int(cols[8]) != 0:
        fan = FAN_HIGH
    if int(cols[9]) != 0:
        fan = FAN_MIDDLE
    if int(cols[10]) != 0:
        fan = FAN_LOW
    if int(cols[11]) != 0:
        fan = FAN_LOW
    if int(cols[15]) != 0:
        fan = FAN_AUTO

    temp = 26
    inputs = []
    for i in range(0, 32):
        inputs.append(abs(int(cols[128 + i])) & 0xFFFF)

    temp = int(inputs[3])

    ac = {}
    ac["ac_mode"] = mode
    ac["fan"] = fan
    ac["temp"] = temp
    return ac


# poll ac status
def poll_status(host, port):
    ac0 = poll_one(host, port, 0)
    online_list = get_online_list(ac0)

    acs = {}
    for acNum in online_list:
        ret = poll_one(host, port, acNum)
        ac = get_ac_info(ret)
        acs[acNum] = ac
    return acs


# pylint: disable=abstract-method
# pylint: disable=too-many-instance-attributes
class Thermostat(ClimateEntity):
    """Representation of a Midea thermostat."""

    def __init__(self, name, ac_name, host, port, acdata):
        """Initialize the thermostat."""
        self._name = "{}_{}".format(name, ac_name)
        self._ac_name = ac_name
        self._ac_id = ac_name
        self._host = host
        self._port = port
        self._attr_hvac_modes = SUPPORT_HVAC
        self._attr_fan_modes = SUPPORT_FAN
        self._attr_fan_mode = FAN_AUTO
        self._attr_temperature_unit = TEMP_CELSIUS
        self._attr_supported_features = SUPPORT_FLAGS
        self._attr_unique_id = f"{name}{host}{port}{ac_name}"
        self._attr_should_poll = True
        self._attr_precision = PRECISION_WHOLE
        self.updateWithAcdata(acdata)
        _LOGGER.debug("Init called")

    def updateWithAcdata(self, acdata):
        """Update self data with acdata"""
        self._attr_current_temperature = acdata["temp"]
        self._attr_target_temperature = acdata["temp"]
        self._attr_hvac_mode = acdata["ac_mode"]
        self._attr_fan_mode = acdata["fan"]

    def update(self):
        """Update the data from the thermostat."""
        ret = poll_one(self._host, self._port, self._ac_name)
        acdata = get_ac_info(ret)
        self.updateWithAcdata(acdata)
        _LOGGER.debug("Update called")

    def setStates(self):
        """Set new target states."""
        state_cmd = CONST_STATE_CMD_MAP[self._attr_hvac_mode]
        fan_cmd = CONST_FAN_CMD_MAP[self._attr_fan_mode]

        # 批量设定 write_reg(0,1,c-49,0,0,0,0,0,0,0,0)
        # 单个设定 write_reg(cur_u*32+1,3,cmode,cfan,ctemp,0,0,0,0,0,0,0,0);

        url = (
            get_base_url(self._host, self._port)
            + "&u="
            + str(self._ac_id)
            + "&f=16&s="
            + str(int(self._ac_id) * 32 + 1)
            + "&c=3"
            + "&p0="
            + str(state_cmd)
            + "&p1="
            + str(fan_cmd)
            + "&p2="
            + str(self._attr_target_temperature)
            + "&p3=0"
            + "&p4=0"
            + "&p5=0"
            + "&p6=0"
            + "&p7=0"
        )

        _LOGGER.info("Set state=%s", url)
        req = requests.get(url, timeout=DEFAULT_TIMEOUT)
        if req.status_code != requests.codes.ok:
            _LOGGER.exception("Error doing API request")
        else:
            _LOGGER.debug("API request ok %d", req.status_code)

    @property
    def name(self):
        """Return the name of the thermostat."""
        return self._name

    @property
    def device_state_attributes(self):
        """Return the device specific state attributes."""
        return {ATTR_MODE: self._attr_hvac_mode}

    def set_temperature(self, **kwargs):
        """Set new target temperature."""
        temperature = kwargs.get(ATTR_TEMPERATURE)
        if temperature is None:
            return
        else:
            self._attr_target_temperature = math.ceil(temperature)
            self.setStates()
            self.async_write_ha_state()

    def set_hvac_mode(self, hvac_mode):
        """Set hvac mode."""
        if hvac_mode not in CONST_STATE_CMD_MAP:
            hvac_mode = HVAC_MODE_OFF
        self._attr_hvac_mode = hvac_mode
        self.setStates()
        self.async_write_ha_state()

    def set_fan_mode(self, fan):
        """Set new target fan mode."""
        if self._attr_hvac_mode == HVAC_MODE_OFF:
            return
        if fan not in CONST_FAN_CMD_MAP:
            fan = FAN_AUTO

        self._attr_fan_mode = fan
        self.setStates()
        self.async_write_ha_state()


# if __name__ == "__main__":
#     host = "192.168.8.200"
#     port = "80"
#     acs = poll_status(host, port)
#     for v in acs:
#         print(acs[v])

#     ac_name = 0
#     ac = {}
#     if len(acs) > 0:
#         ac = acs.get(2)

#     entity = Thermostat("midea", 2, host, port, ac)
#     entity.setStates()
